package com.youyi.utils;

import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 雪花算法实现类，用于生成全局唯一的64位ID。
 */
public class SnowflakeIdUtils {

    private static final Logger LOGGER = Logger.getLogger(SnowflakeIdUtils.class.getName());

    // 时间起点 (2024-01-01)
    private static long EPOCH = 0L;

    // 每一部分所占的位数
    private static final long WORKER_ID_BITS = 5L;
    private static final long DATA_CENTER_ID_BITS = 5L;
    private static final long SEQUENCE_BITS = 12L;

    // 每一部分的最大值
    private static final long MAX_WORKER_ID = -1L ^ (-1L << WORKER_ID_BITS);
    private static final long MAX_DATA_CENTER_ID = -1L ^ (-1L << DATA_CENTER_ID_BITS);
    private static final long SEQUENCE_MASK = -1L ^ (-1L << SEQUENCE_BITS);

    // 每一部分向左移位数
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    private static final long DATA_CENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    private static final long TIMESTAMP_LEFT_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATA_CENTER_ID_BITS;

    // 单例实例的状态变量
    private static final AtomicLong sequence = new AtomicLong(0);
    private static volatile long lastTimestamp = -1L;

    // 默认的工作机器ID和数据中心ID，可以根据需要调整或通过配置文件读取
    private static final long workerId;
    private static final long dataCenterId;

    // 静态块用于初始化单例和配置
    static {
        try {
            // 设置默认值或者从配置文件/环境变量读取
            workerId = 1;
            dataCenterId = 1;

            if (workerId > MAX_WORKER_ID || workerId < 0) {
                throw new IllegalArgumentException(String.format("Worker Id can't be greater than %d or less than 0", MAX_WORKER_ID));
            }
            if (dataCenterId > MAX_DATA_CENTER_ID || dataCenterId < 0) {
                throw new IllegalArgumentException(String.format("Data Center Id can't be greater than %d or less than 0", MAX_DATA_CENTER_ID));
            }
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Failed to initialize SnowflakeIdGenerator", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 生成下一个全局唯一的ID（静态方法）。
     *
     * @return 下一个全局唯一的ID
     */
    public static synchronized long nextId() {
        long timestamp = timeGen();

        // 如果当前时间小于上次生成ID的时间戳，则表示时钟回拨过，等待直到时钟恢复
        if (timestamp < lastTimestamp) {
            LOGGER.warning(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            while (timestamp < lastTimestamp) {
                timestamp = timeGen();
            }
        }

        // 如果在同一毫秒内，则递增序列号
        if (lastTimestamp == timestamp) {
            long seq = sequence.getAndIncrement();
            if (seq >= SEQUENCE_MASK) {
                // 序列号溢出，等待下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
                sequence.set(0); // Reset sequence
            }
        } else {
            // 不是同一毫秒，重置序列号为0
            sequence.set(0);
        }

        lastTimestamp = timestamp;

        // 将各部分拼接成64位ID
        return ((timestamp - EPOCH) << TIMESTAMP_LEFT_SHIFT) |
                (dataCenterId << DATA_CENTER_ID_SHIFT) |
                (workerId << WORKER_ID_SHIFT) |
                sequence.get();
    }

    /**
     * 得到下一个id
     * @param epoch 开始时间
     * @param workerId 工作机器id(默认为1)
     * @param dataCenterId 数据中心id(默认为1)
     * @return
     */
    public static synchronized long nextId(long epoch , long workerId , long dataCenterId) {
        long timestamp = timeGen();

        // 如果当前时间小于上次生成ID的时间戳，则表示时钟回拨过，等待直到时钟恢复
        if (timestamp < lastTimestamp) {
            LOGGER.warning(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
            while (timestamp < lastTimestamp) {
                timestamp = timeGen();
            }
        }

        // 如果在同一毫秒内，则递增序列号
        if (lastTimestamp == timestamp) {
            long seq = sequence.getAndIncrement();
            if (seq >= SEQUENCE_MASK) {
                // 序列号溢出，等待下一毫秒
                timestamp = tilNextMillis(lastTimestamp);
                sequence.set(0); // Reset sequence
            }
        } else {
            // 不是同一毫秒，重置序列号为0
            sequence.set(0);
        }

        lastTimestamp = timestamp;

        // 将各部分拼接成64位ID
        return ((timestamp - epoch) << TIMESTAMP_LEFT_SHIFT) |
                (dataCenterId << DATA_CENTER_ID_SHIFT) |
                (workerId << WORKER_ID_SHIFT) |
                sequence.get();
    }


    /**
     * 等待直到获取到比上次更大的时间戳。
     *
     * @param lastTimestamp 上次生成ID的时间戳
     * @return 新的时间戳
     */
    private static long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 获取当前时间戳。
     *
     * @return 当前时间戳
     */
    private static long timeGen() {
        return System.currentTimeMillis();
    }

    //--------------- 测试 ---------------
    public static void main(String[] args) {


        System.out.println(System.currentTimeMillis());
        System.out.println(SnowflakeIdUtils.nextId());
        System.out.println(SnowflakeIdUtils.nextId());
        System.out.println(SnowflakeIdUtils.nextId());
        System.out.println(SnowflakeIdUtils.nextId());
        System.out.println(SnowflakeIdUtils.nextId());
        System.out.println( "@" + SnowflakeIdUtils.nextId(  1724961578789L  ,1 , 1));
        System.out.println(System.currentTimeMillis());

    }
}